/**
 * Copyright (c) 2009 Koder Chen. All Rights Reserved.
 *
 * main.c
 *
 * The file is the entrance of the rsa system. It is responsible for the
 * anaylyse of the command line, and the initialization of the system
 *
 *  Author: chenfuhan
 * Created: 2009-6-23
 * Version: 1.0
 */
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>

#include "type.h"
#include "prime.h"
#include "tool.h"
#include "bigint.h"
#include "rsa.h"
#include "getopt.h"

// THE ACTIONS
const int DO_GENKEYS = 0;
const int DO_ENCRYPT = 1;
const int DO_DECRYPT = 2;

void logmsg(char* output)
{
	printf("[Message] %s\n", output);
	fflush(stdout);
}

void init()
{
	jacobi_a = rsa_default_malloc();
	jacobi_b = rsa_default_malloc();
	jacobi_r = rsa_default_malloc();
	jacobi_o = rsa_default_malloc();
	jacobi_t1= rsa_default_malloc();
	jacobi_t2= rsa_default_malloc();
	jacobi_x    = rsa_default_malloc();
	jacobi_min  = rsa_default_malloc();
	jacobi_nt   = rsa_default_malloc();
	jacobi_nh   = rsa_default_malloc();
	bi_mod_t    = rsa_default_malloc();
	bi_mul_t    = rsa_malloc_ubyte(2 * MAX_BYTES);
	bi_div_pt   = rsa_default_malloc();
	bi_div_qt   = rsa_default_malloc();
}

void uninit()
{
	rsa_free(jacobi_a);
	rsa_free(jacobi_b);
	rsa_free(jacobi_r);
	rsa_free(jacobi_o);
	rsa_free(jacobi_t1);
	rsa_free(jacobi_t2);
	rsa_free(jacobi_x);
	rsa_free(jacobi_min);
	rsa_free(jacobi_nt);
	rsa_free(jacobi_nh);
	rsa_free(bi_mod_t);
	rsa_free(bi_mul_t);
	rsa_free(bi_div_pt);
	rsa_free(bi_div_qt);
}

void encrypt(char* infile, char* outfile, char* key)
{
	logmsg("Now encrypting the message...");

	FILE *fpKey = fopen(key, "r");
	FILE *fpIn  = fopen(infile, "r");
	FILE *fpOut = fopen(outfile, "w");
	if(fpKey && fpIn && fpOut)
	{
		rsa_key keys;
		fread(&keys, sizeof(keys), 1, fpKey);
		fflush(fpKey);
		MAX_BYTES = keys.max_bytes;
		MAX_BITS  = keys.max_bits;

		// initialize the envoriment
		init();

		// fetch the length of the input text file
		long bytes = 0;
		fseek(fpIn, 0, SEEK_END);
		bytes = ftell(fpIn);
		rewind(fpIn);

		u8_pt msg = rsa_malloc_ubyte(bytes * 4);
		fread(msg, sizeof(char), bytes, fpIn);
		fflush(fpIn);

		// encrypt the string, with the function pointer pointing to rsa_encrypt_bigint
		// initialize the envoriment
		init();
		i32_t size = rsa_chopstring(msg, keys.e, keys.n, msg, bytes, rsa_encrypt_bigint); 
		// uninitialize the environment
		uninit();

		fwrite(msg, sizeof(char), size, fpOut);
		fflush(fpOut);
		rsa_free(msg);
	}
	if(fpKey) fclose(fpKey);
	if(fpIn)  fclose(fpIn);
	if(fpOut) fclose(fpOut);

	logmsg("Now end decryping the message.");
}

void decrypt(char* infile, char* outfile, char* key)
{
	logmsg("Now decryping the message...");

	FILE *fpKey = fopen(key, "r");
	FILE *fpIn  = fopen(infile, "r");
	FILE *fpOut = fopen(outfile, "w");
	if(fpKey && fpIn && fpOut)
	{
		rsa_key keys;
		fread(&keys, sizeof(keys), 1, fpKey);
		fflush(fpKey);
		MAX_BYTES = keys.max_bytes;
		MAX_BITS  = keys.max_bits;

		// fetch the length of the input text file
		long bytes = 0;
		fseek(fpIn, 0, SEEK_END);
		bytes = ftell(fpIn);
		rewind(fpIn);

		u8_pt msg = rsa_malloc_ubyte(bytes + 1);
		fread(msg, sizeof(char), bytes, fpIn);
		fflush(fpIn);

		// encrypt the string, with the function pointer pointing to rsa_encrypt_bigint
		// initialize the envoriment
		init();
		i32_t size = rsa_gluechops(msg, keys.d, keys.n, msg, bytes, rsa_decrypt_bigint); 
		// uninitialize the environment
		uninit();

		fwrite(msg, sizeof(char), size, fpOut);
		fflush(fpOut);
		rsa_free(msg);
	}
	if(fpKey) fclose(fpKey);
	if(fpIn)  fclose(fpIn);
	if(fpOut) fclose(fpOut);

	logmsg("Now end the decrypting of the message.");
}


void genkeys(int bits, char* outfile)
{
	logmsg("Now generating the keys...");

	FILE *fp = fopen(outfile, "w+");
	if(fp)
	{
		rsa_key keys;
		memset(&keys, 0, sizeof(keys));

		if(bits < 16 && bits > 128)
		{
			logmsg("The value of bits should be in [16, 128]");
			exit(0);
		}
		MAX_BYTES = ((bits + 7) / 8) * 4;
		MAX_BITS  = MAX_BYTES * 8;
		keys.max_bytes = MAX_BYTES;
		keys.max_bits = MAX_BITS;
		// initialize the envoriment
		init();
		logmsg("Call rsa_gen_keys...");
		rsa_gen_keys(bits, &keys);
		logmsg("Write to file...");
		uninit();

		fwrite(&keys, sizeof(keys), 1, fp);
		fflush(fp);
		fclose(fp);
	}

	logmsg("Now end the generation.");
}

void help()
{
	printf(
		"+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"
		"+ RSA Encrypt/Decrypt System Version 1.0                                +\n"
		"+ Copyright (c) 2009 Koder Chen. All Rights Reserved.                   +\n"
		"+ Email: chenfh04@mails.tsinghua.edu.cn                                 +\n"
		"+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n\n"
		"+Usage: rsa [OPTION] [INPUT] \n"
		" -h       print this help information list\n"
		" -g       generate the public/private keys\n"
		" -e       encrypt a message\n"
		" -d       decrypt a message\n"
		" -o file  output file holding the encrypted/decrypted message\n"
		" -i file  input file be encrypted/decrypted\n"
		" -k file  specified the input file for keys, that is, the file's\n"
		"          that stores the content of all public/private keys\n"
		" -b bits  specify the number of bits of the value of 'p' and 'q' in rsa\n"
		"          system, it will normally be 8, 64, 128, 512, 1024. And\n"
		"          For the current version, it should not be larger than\n"
		"          128, than the bits of 'n' can reach 256.\n"
		"\n"
		"+Some samples:\n"
		"rsa -g -b 32 -o keys.txt\n"
		"          means generate keys in 32 bits and stores all the information\n"
		"          into file keys.txt\n"
		"\n"
		"rsa -e -i msg1.txt -o msg2.txt -k keys.txt\n"
		"          means encrypt the content in file msg1.txt into file msg2.txt\n"
		"          with the keys stores in file keys.txt\n"
		"\n"
		"rsa -d -i msg1.txt -o msg2.txt -k keys.txt\n"
		"         means decrypt the content in file msg1.txt into file msg2.txt\n"
		"         with the keys stores in file keys.txt\n"
	);
}

int main(int argc, char* argv[])
{
	int choice	 = 0;
	int bits     = 0;
	char* output = NULL;
	char* input  = NULL;
	char* key    = NULL;

	while(1) {
		int c = getopt(argc, argv, "-hgedi:o:k:b:");
		if(c == -1) 
		{
			//printf("Invalid input parameters\n");
			break;
		}

		switch(c) {
			case 'h': help(); exit(0);
			case 'g': choice = DO_GENKEYS;   break;
			case 'e': choice = DO_ENCRYPT;   break;
			case 'd': choice = DO_DECRYPT;   break;
			case 'o': output = optarg;       break;
			case 'i': input  = optarg;       break;
			case 'k': key    = optarg;       break;
			case 'b': bits   = atoi(optarg); break;
			default:  break;
		};
	}

	switch(choice)
	{
	case 0: genkeys(bits, key);				break;
	case 1: encrypt(input, output, key);	break;
	case 2: decrypt(input, output, key);	break;
	default:
		break;
	};
	return 0;
}
